package cn.nkpro.elcube.platform.service.third;


import cn.nkpro.elcube.exception.NkSystemException;
import cn.nkpro.elcube.platform.model.NkShortMessageResult;
import cn.nkpro.elcube.platform.service.NkShortMessageService;
import cn.nkpro.elcube.platform.service.PlatformRegistryService;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.ParseException;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Random;

public class EntinfoShortMessageServiceImpl implements NkShortMessageService, InitializingBean {

    @SuppressWarnings("all")
    @Autowired
    private PlatformRegistryService registryService;

    @Value("${cn.entinfo.url:}")
    private String url;
    @Value("${cn.entinfo.sn:}")
    private String sn;
    @Value("${cn.entinfo.password:}")
    private String password;
    @Value("${cn.entinfo.affix:}")
    private String affix;
    @Value("${cn.entinfo.ver-code-template:您本次操作的验证码是[code]，有效时间为3分钟。该验证码用于登录使用，切勿将验证码泄露给他人。重要提醒：任何索要验证码的都是骗子，千万别给！}")
    private String verCodeTemplate;

    private String pwd;

    @Override
    public String sendVerCode(String phone) {
        String format = String.format("%04d", new Random().nextInt(9999));
        String response = sendMsg(phone, verCodeTemplate.replace("[code]",format), "", "", "", "");
        NkShortMessageResult result = getResult(response);
        if(result.getStatus()==NkShortMessageResult.Status.SUCCESS){
            return format;
        }
        throw new NkSystemException("短信发送失败："+result.getMessage());
    }

    @Override
    public NkShortMessageResult sendTemplateMessage(String phone, String templateCode, Map<String, String> templateParams) {
        String templateString = stitchingParameters(templateCode, templateParams);
        String response = sendMsg(phone, templateString, "", "", "", "");
        return getResult(response);
    }

    @Override
    public NkShortMessageResult sendTemplateMessage(List<String> phoneList, String templateCode, Map<String,String> templateParams) {
        String templateString = stitchingParameters(templateCode, templateParams);
        String phone = String.join(",", phoneList);
        String response = sendMsg(phone, templateString, "", "", "", "");
        return getResult(response);
    }

    @Override
    public NkShortMessageResult sendTemplateMessage(List<String> phoneList, String templateCode, List<Map<String,String>> templateParams) {
        NkShortMessageResult results = new NkShortMessageResult();
        List<NkShortMessageResult> list = new ArrayList<>();
        for (int i = 0; i < phoneList.size(); i++) {
            String templateString = stitchingParameters(templateCode, templateParams.get(i));
            String response = sendMsg(phoneList.get(i), templateString, "", "", "", "");
            NkShortMessageResult result = getResult(response);
            list.add(result);
        }
        results.setDetails(list);
        return results;
    }

    private String stitchingParameters(String templateCode,Map<String,String> templateParams){
        String templateString = registryService.getString("@DICT", templateCode);
        for (Map.Entry<String,String> l : templateParams.entrySet()){
            String key = l.getKey();
            String value = l.getValue();
            templateString = templateString.replace("["+key+"]",value);
        }
        return templateString;
    }

    private NkShortMessageResult getResult (String response){
        NkShortMessageResult result = new NkShortMessageResult();
        StringBuilder sb = new StringBuilder();
        sb.append(response);
        if (!response.contains("-") && response.length() <= 18){
            result.setStatus(NkShortMessageResult.Status.SUCCESS);
        }else{
            switch (response){
                case "-10" :
                    sb.append("内容超长");
                    break;
                case "-12" :
                    sb.append("账号状态错误");
                    break;
                case "-21" :
                case "-22" :
                    sb.append("IP 鉴权失败");
                    break;
                case "-211" :
                    sb.append("IP 限制");
                    break;
                case "-23" :
                    sb.append("账号信息错误");
                    break;
                case "-601" :
                    sb.append("参数错误");
                    break;
                case "-603" :
                    sb.append("密码参数错误");
                    break;
                case "-604" :
                    sb.append("手机号参数错误");
                    break;
                case "-605" :
                    sb.append("内容参数错误");
                    break;
                case "-606" :
                    sb.append("扩展参数错误");
                    break;
                case "-607" :
                    sb.append("扩展必须为数字");
                    break;
                case "-608" :
                    sb.append("定时时间参数错误");
                    break;
                case "-609" :
                    sb.append("Rrid 参数错误");
                    break;
                case "-610" :
                    sb.append("Rrid 非数字");
                    break;
                case "-611" :
                    sb.append("内容编码错误");
                    break;
                case "-623" :
                    sb.append("手机号个数与内容不匹配");
                    break;
                case "-624" :
                    sb.append("扩展数与手机号数不符");
                    break;
                case "-625" :
                    sb.append("定时数与号码数不符");
                    break;
                case "-626" :
                    sb.append("Rrid 数与号码数不符");
                    break;
                default:sb.append("未知错误");
            }
            result.setStatus(NkShortMessageResult.Status.ERR);
        }
        result.setMessage(sb.toString());
        return result;
    }

    @SuppressWarnings("all")
    private String sendMsg(String mobile, String content, String ext, String stime, String rrid, String msgfmt) {
        String result;
        try {
            content = URLEncoder.encode(affix+content, "utf-8");

            HttpPost post = new HttpPost(url);
            post.setHeader("Content-Type", "application/x-www-form-urlencoded");

            List<NameValuePair> nvps = new ArrayList<>();
            nvps.add(new BasicNameValuePair("sn", sn));
            nvps.add(new BasicNameValuePair("pwd", pwd));
            nvps.add(new BasicNameValuePair("mobile", mobile));
            nvps.add(new BasicNameValuePair("content", content));
            nvps.add(new BasicNameValuePair("ext", ext));
            nvps.add(new BasicNameValuePair("stime", stime));
            nvps.add(new BasicNameValuePair("rrid", rrid));
            nvps.add(new BasicNameValuePair("msgfmt", msgfmt));

            try {
                post.setEntity(new UrlEncodedFormEntity(nvps, "UTF-8"));
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }

            CloseableHttpClient httpClient = createSSLClientDefault();

            HttpResponse response = httpClient.execute(post);
            result = getResponseMessage(response);

        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException(e.getMessage());
        }
        return result;
    }

    private static CloseableHttpClient createSSLClientDefault() {
        try {
            SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, (chain, authType) -> true).build();
            HostnameVerifier hostnameVerifier = NoopHostnameVerifier.INSTANCE;
            SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, hostnameVerifier);
            return HttpClients.custom().setSSLSocketFactory(sslsf).build();
        } catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException e) {
            e.printStackTrace();
        }
        return HttpClients.createDefault();
    }

    private static String getResponseMessage(HttpResponse response) throws ParseException, IOException {
        HttpEntity entity = response.getEntity();
        String resp = EntityUtils.toString(entity, "UTF-8");
        EntityUtils.consume(entity);
        return resp;
    }

    @Override
    public void afterPropertiesSet(){
        if (StringUtils.isBlank(this.affix)){
            System.err.println("签署名为空请到配置文件中添加cn.entinfo.affix");
            System.exit(0);
        }
        if (StringUtils.isBlank(this.password)){
            System.err.println("密码为空请到配置文件中添加cn.entinfo.password");
            System.exit(0);
        }
        if (StringUtils.isBlank(this.sn)){
            System.err.println("账号为空请到配置文件中添加cn.entinfo.sn");
            System.exit(0);
        }
        if (StringUtils.isBlank(this.url)){
            System.err.println("请求地址为空请到配置文件中添加cn.entinfo.url");
            System.exit(0);
        }
        this.pwd = setPwd();
    }

    private String setPwd(){
        String sourceStr = sn+password;
        StringBuilder resultStr = new StringBuilder();
        try {
            byte[] temp = sourceStr.getBytes();
            MessageDigest md5 = MessageDigest.getInstance("MD5");
            md5.update(temp);
            byte[] b = md5.digest();
            for (byte value : b) {
                char[] digit = {'0', '1', '2', '3', '4', '5', '6', '7', '8',
                        '9', 'A', 'B', 'C', 'D', 'E', 'F'};
                char[] ob = new char[2];
                ob[0] = digit[(value >>> 4) & 0X0F];
                ob[1] = digit[value & 0X0F];
                resultStr.append(new String(ob));
            }
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return resultStr.toString();
    }



}
